package com.jetinno.syrup2.upgrade

import android.content.Context
import android.os.SystemClock
import android.util.Log
import com.jetinno.hex.HexTrans
import com.jetinno.syrup2.upgrade.*
import com.jetinno.utils.LogUtils
import com.jetinno.syrup2.SyrupProtocol
import com.jinuo.mhwang.util.CRC16
import java.io.BufferedInputStream
import java.io.File
import java.io.FileInputStream

/**
 * 升级管理
 */
object UpgradeManager {
    val TAG = "UpgradeManager"

    /***
     *  升级固件
     * @param path
     *          要安装的apk文件的路径
     * @param firmware
     *          要升级的固件类型
     * @return 安装成功返回true，安装失败返回false。
     */
    @JvmStatic
    fun upgradeFirmware(path: String, callback: SyrupUpgradeCallback): Boolean {
        callback?.onUpgradeStart()
        val file = File(path)
        if (!file.exists() || !file.isFile) {
            callback?.onUpgradeError(REASON_1)
            LogUtils.d2File(TAG, "$path 固件文件不存在")
            return false
        }
        SystemClock.sleep(2000) // 增加到6s的时间，等待所有使用io的服务停止
        if (!file.canRead()) {
            callback?.onUpgradeError(REASON_2)
            LogUtils.d2File(TAG, "$path 文件读取失败")
            return false
        }
        callback?.onUpgradeStart()
        //首先发送握手指令

        val operation = SyrupProtocol.instance
        var respond = operation.upgradeFirmware(UPGRADE_STEP_HANDSHAKE)
        LogUtils.d2File(TAG, "upgradeFirmware UPGRADE_STEP_HANDSHAKE:" + respond.toString())
//        var jsonBean = mGson.fromJson(respond.jsonResult, RespondCmd::class.java)
        //握手失败
        if (respond.second != UPGRADE_STEP_HANDSHAKE_RESPOND) {
            callback.onUpgradeError(REASON_3, "${respond.second}")

            LogUtils.d2File(TAG, "$path Handshake failed.")
            return false
        }
//        respond = operation.receiveData(UpgradeConst.FIRMWARE_UPGRADE)

        respond = operation.upgradeFirmware(UPGRADE_STEP_CHOOSE_IO)


        LogUtils.d2File(TAG, "upgradeFirmware UPGRADE_STEP_CHOOSE_IO:" + respond.toString())
        //发送IO升级指令成功
        if (respond.second != UPGRADE_STEP_CHOOSE_IO_RESPOND) {
            callback.onUpgradeError(REASON_4, "${respond.second}")

            LogUtils.d2File(TAG, "$path 发送升级指令后收到错误回复")
            return false
        }
        //YMODEM协议
        var seq_no = 0
        SystemClock.sleep(200)
//
        var responds = operation.reads()
        LogUtils.e(TAG, "test------" + responds)
//        //收到起始字符
//        if (!responds.equals(STR_C)) {
//            LogUtils.d2File(TAG,"$path Have't read char 'C'.")
//            return false
//        }
        var inputStream: BufferedInputStream? = null
        var totalSize: Long = 0
        var currentSize: Long = 0
        try {
            inputStream = BufferedInputStream(FileInputStream(file), Context.MODE_WORLD_READABLE)
            totalSize = inputStream.available().toLong()
            LogUtils.e(TAG, "升级文件大小$totalSize")
            //升级文件大小不为空
            if (totalSize <= 0) {
                callback?.onUpgradeError(REASON_5)
                LogUtils.d2File(TAG, "$path 文件为空.")
                return false
            }
            seq_no++
            val ymodemHead = packYmodemHead(file.name, totalSize)
            if (ymodemHead == null || ymodemHead.isEmpty()) {
                callback?.onUpgradeFinish(false, REASON_6)
                LogUtils.d2File(TAG, "$path 文件名过长")
                return false
            }
            var retry = 0
            while (true) {
                if (retry++ > UPGRADE_MAX_RETRY) {
                    callback?.onUpgradeFinish(false, REASON_7)
                    return false
                }
                operation.reads()
                LogUtils.e(TAG, "test--" + responds)
                respond = operation.upgradeFirmware1(HexTrans.bytes2HexString(ymodemHead, ymodemHead?.size!!))
                if (respond.second.equals(STR_ACK + STR_C + STR_C + STR_C)) break
                if (respond.second.equals(STR_ACK + STR_C)) break
                if (respond.second.equals(STR_NAK)) {
                    SystemClock.sleep(100)
                    continue
                }
                return false
            }
            var byteArray = ByteArray(BLOCK_SIZE_1024)
            var hasSent = true
            var len = 0
            retry = 0
            while (true) {
                LogUtils.e(TAG, "test" + 1)
                if (retry++ > UPGRADE_MAX_RETRY) {
                    callback?.onUpgradeFinish(false, REASON_7)
                    LogUtils.d2File(TAG, "$path 未知错误")
                    return false
                }
                if (hasSent) len = inputStream?.read(byteArray)
                if (len < 0) break
                val ymodemDatagram = packYmodemDatagram(seq_no++, byteArray, 0, len)
                operation.reads()
                respond = operation.upgradeFirmware1(HexTrans.bytes2HexString(ymodemDatagram, ymodemDatagram.size))
                LogUtils.e(TAG, "test" + respond.second)

                SystemClock.sleep(50)
                if (respond.second.equals(STR_ACK + STR_C)) {
                    hasSent = true
                    retry = 0
                    currentSize += len
                    callback?.onUpgradeProgress(((currentSize * 100 / totalSize) as Number).toInt())
                    continue
                }
                if (respond.second.equals(STR_ACK)) {
                    hasSent = true
                    retry = 0
                    currentSize += len
                    callback?.onUpgradeProgress(((currentSize * 100 / totalSize) as Number).toInt())
                    continue
                }
                if (respond.second.equals(STR_NAK)) {
                    hasSent = false
                    SystemClock.sleep(50)
                    continue
                }
                callback?.onUpgradeFinish(false, REASON_7)
                return false
            }
        } catch (e: Exception) {
            callback?.onUpgradeFinish(false, REASON_8)
            LogUtils.d2File(TAG, Log.getStackTraceString(e))
        } finally {
            if (inputStream != null) {
                inputStream.close()
            }
        }
        callback?.onUpgradeProgress(100)
        operation.upgradeFirmware(STR_EOT)
        val emptyYmodemHead = packEmptyYmodemHead()
        respond = operation.upgradeFirmware(HexTrans.bytes2HexString(emptyYmodemHead, emptyYmodemHead.size))
        var hexCmd = respond.second
        LogUtils.e(TAG, "test结束--" + hexCmd)

        while (hexCmd.equals(STR_ACK) || hexCmd.equals(STR_C) || hexCmd.equals(STR_ACK + STR_C)) {
            respond = operation.upgradeFirmware(HexTrans.bytes2HexString(emptyYmodemHead, emptyYmodemHead.size))
            LogUtils.e(TAG, "查询成功指令--" + respond.second)
            //回复中有成功指令就算升级成功
            if (respond.second.contains(UPGRADE_STEP_SUCCESS)) {
                callback?.onUpgradeFinish(true)
                return true

            }


//            hexCmd = operation.reads()
        }
        LogUtils.e("test升级成功", hexCmd)
        if (hexCmd.isNullOrBlank()) {
            callback?.onUpgradeFinish(false, REASON_9)
            LogUtils.d2File(TAG, "$path No response during upgrade.")
            return false
        }
        //回复中有成功指令就算升级成功
        if (hexCmd.contains(UPGRADE_STEP_SUCCESS)) {
            callback?.onUpgradeFinish(true)
            return true
        }

        return false
    }

    private fun packYmodemHead(file_name: String, file_size: Long): ByteArray? {
        val file = file_name.toByteArray()
        val fname_size = file.size
        if (fname_size > UpgradeConst.MAX_FILE_NAME_SIZE) {
            return null
        }
        val result = ByteArray(1 + 2 + UpgradeConst.MAX_BLOCK_SIZE + 2)
        var pos = 0
        result[pos++] = UpgradeConst.SOH
        result[pos++] = 0
        result[pos++] = 0xFF.toByte()
        System.arraycopy(file, 0, result, pos, fname_size)
        val file_length = file_size.toString().toByteArray()
        System.arraycopy(file_length, 0, result, pos + fname_size + 1, file_length.size)
        val crc = CRC16.calcCrc16FromC(result, pos, UpgradeConst.MAX_BLOCK_SIZE)
        pos += UpgradeConst.MAX_BLOCK_SIZE
        result[pos++] = (crc shr 8 and 0xFF).toByte()
        result[pos++] = (crc and 0xFF).toByte()
        return result
    }

    private fun packYmodemDatagram(order: Int, data: ByteArray, offset: Int, length: Int): ByteArray {
        var len = length
        if (len > UpgradeConst.MAX_1K_BLOCK_SIZE) {
            len = UpgradeConst.MAX_1K_BLOCK_SIZE
        }
        val result = ByteArray(1 + 2 + UpgradeConst.MAX_1K_BLOCK_SIZE + 2)
        var pos = 0
        result[pos++] = UpgradeConst.STX
        result[pos++] = (order and 0xFF).toByte()
        result[pos++] = (order xor 0xFF and 0xFF).toByte()
        System.arraycopy(data, offset, result, pos, len)
        val crc = CRC16.calcCrc16FromC(result, pos, UpgradeConst.MAX_1K_BLOCK_SIZE)
        pos += UpgradeConst.MAX_1K_BLOCK_SIZE
        result[pos++] = (crc shr 8 and 0xFF).toByte()
        result[pos++] = (crc and 0xFF).toByte()
        return result
    }

    private fun packEmptyYmodemHead(): ByteArray {
        val result = ByteArray(1 + 2 + UpgradeConst.MAX_BLOCK_SIZE + 2)
        var pos = 0
        result[pos++] = UpgradeConst.SOH
        result[pos++] = 0
        result[pos++] = 0xFF.toByte()
        val crc = CRC16.calcCrc16FromC(result, pos, UpgradeConst.MAX_BLOCK_SIZE)
        pos += UpgradeConst.MAX_BLOCK_SIZE
        result[pos++] = (crc shr 8 and 0xFF).toByte()
        result[pos++] = (crc and 0xFF).toByte()
        return result
    }


}